#pragma once

#include <boost/cstdint.hpp>
#include <boost/functional/hash.hpp>

struct geodata_t;

typedef boost::uint64_t vertex_id_t;
typedef geodata_t vertex_data_t;

enum edge_type_t
{
  EDGE_NORMAL,
  EDGE_REVERSED
};

vertex_id_t const INVALID_VERTEX_ID = -1L;

struct geodata_t
{
  geodata_t(double lon, double lat) :
    lon(lon), lat(lat)
  {
  }

  geodata_t(geodata_t const & source) :
    lon(source.lon), lat(source.lat)
  {
  }

  void swap(geodata_t & other)
  {
    std::swap(lon, other.lon);
    std::swap(lat, other.lat);
  }

  geodata_t const & operator = (geodata_t const & other)
  {
    if (this != &other)
      geodata_t(other).swap(*this);
    return *this;
  }

  double lon;
  double lat;
};

inline bool operator == (geodata_t const & lhs, geodata_t const & rhs)
{
  return lhs.lon == rhs.lon && lhs.lat == rhs.lat;
}

struct vertex_t
{
  vertex_t(vertex_id_t id, vertex_data_t const & data) :
    id(id), data(data)
  {
  }

  vertex_t(vertex_t const & source) :
    id(source.id), data(source.data)
  {
  }

  // Constructs vertex with invalid id
  vertex_t() :
    id(INVALID_VERTEX_ID), data(0.0, 0.0)
  {
  }

  inline bool is_valid() const
  {
    return id != INVALID_VERTEX_ID;
  }

  vertex_id_t id;
  vertex_data_t data;
};

inline bool operator == (vertex_t const & lhs, vertex_t const & rhs)
{
  if (lhs.is_valid() && rhs.is_valid())
    return lhs.id == rhs.id;
  else
    return lhs.data == rhs.data;
}

inline bool operator != (vertex_t const & lhs, vertex_t const & rhs)
{
  return !(lhs == rhs);
}

inline size_t hash_value(vertex_t const & val)
{
  boost::hash<int> hasher;
  return hasher(val.id);
}

struct edge_t
{
  edge_t(vertex_id_t source, vertex_id_t destination, double weight) :
    source(source), destination(destination), weight(weight)
  {
  }

  edge_t(edge_t const & source) :
    source(source.source), destination(source.destination),
        weight(source.weight)
  {
  }

  // Constructs edge with invalid id
  edge_t() :
    source(INVALID_VERTEX_ID), destination(INVALID_VERTEX_ID)
  {
  }

  inline bool is_valid() const
  {
    return source != INVALID_VERTEX_ID && destination != INVALID_VERTEX_ID;
  }
  vertex_id_t source;
  vertex_id_t destination;
  double weight;
};

double orthodromic_distance(vertex_data_t const & v1, vertex_data_t const & v2);